/*
 * Copyrignt (c) xuzeshui.com. All Rights Reserved.
 * Author: Zeshui Xu<i@xuzeshui.com>
 * Created Time: 2015-11-08 15:53:19
 * Last Modified: 2016-05-04 15:45:12
 * File Name: account/accountbind.go
 * Description:
 */
package account

import (
	"fmt"
	"time"

	"passport/errorcode"
	"passport/models"
	"passport/xfile"

	log "github.com/cihub/seelog"
	"github.com/garyburd/redigo/redis"
)

/*
	账号绑定请求
*/
type AccountBindInfoReq struct {
	ExtFrom  string `json:"ext_from"`
	ExtId    string `json:"ext_id"`
	NickName string `json:"nick_name"`
}

/*
	MobileAccountBindReq
*/
type MobileAccountBindReq struct {
	Mobile   string `json:"mobile"`
	Captcha  string `json:"captcha"`
	Password string `json:"password"`
}

/*
	AccountBindService
*/
type AccountBindService struct{}

/*
	账号绑定的redis key格式
*/
func (p *AccountBindService) GetUserAccountBindKey(userid string) string {
	return fmt.Sprintf("b:%s", userid)
}

/*
	将extFrom,extId绑定到userid纪录中,方便查询
	以userid做为hash key,extFrom作为field
*/
func (p *AccountBindService) RecordUserAccountBind(userid, extFrom, extId string) error {
	redisKey := p.GetUserAccountBindKey(userid)
	conn := models.GetPassportModel().Get()
	defer conn.Close()
	_, err := conn.Do("hset", redisKey, extFrom, extId)
	return err
}

/*
	查询账号类型是否已有绑定纪录
*/
func (p *AccountBindService) existsUserAccountBind(userid, extFrom string) bool {
	redisKey := p.GetUserAccountBindKey(userid)
	conn := models.GetPassportModel().Get()
	defer conn.Close()
	if reply, err := redis.Bool(conn.Do("hexists", redisKey, extFrom)); err == nil {
		return reply
	} else {
		log.Warnf("existsUserAccountBind uid:%s, extFrom:%s, error:%s", userid, extFrom, err.Error())
	}
	return true
}

/*
	将userid纪录中的绑定信息删除,方便查询
*/
func (p *AccountBindService) RecordUserAccountUnbind(userid, extFrom string) error {
	redisKey := p.GetUserAccountBindKey(userid)
	conn := models.GetPassportModel().Get()
	defer conn.Close()
	_, err := conn.Do("hdel", redisKey, extFrom)
	return err
}

/*
	查询某个userid当前绑定了哪些关联账号
*/
func (p *AccountBindService) GetBindedAccountForUserid(userid string) (map[string]string, error) {
	redisKey := p.GetUserAccountBindKey(userid)
	conn := models.GetPassportModel().Get()
	defer conn.Close()
	if reply, err := redis.StringMap(conn.Do("hgetall", redisKey)); err == nil {
		return reply, nil
	} else if redis.ErrNil == nil {
		return make(map[string]string), nil
	} else {
		return nil, err
	}
}
func (p *AccountBindService) GetBindedAccountUserid(userid string) ([]interface{}, error) {
	redisKey := p.GetUserAccountBindKey(userid)
	conn := models.GetPassportModel().Get()
	defer conn.Close()
	if reply, err := redis.Values(conn.Do("hgetall", redisKey)); err == nil {
		return reply, nil
	} else if redis.ErrNil == nil {
		return nil, nil
	} else {
		return nil, err
	}
}
/*
   账号绑定
*/
func (p *AccountBindService) AccountBind(uid string, isThirdAccount bool, req *AccountBindInfoReq) (uint, string) {
	if p.existsUserAccountBind(uid, req.ExtFrom) {
		log.Warnf("uid: %s bind %s yet", uid, req.ExtFrom)
		return errorcode.EC_ACCOUNT_BIND_BINDED_BY_OTHER, "extFrom binded yet"
	}
	accountInfo, err := GetAccountService().GetAllAccountInfo(req.ExtFrom, req.ExtId)
	if err != nil {
		log.Errorf("GetAllAccountInfo extFrom: %s, extId: %s error: %s", req.ExtFrom, req.ExtId, err.Error())
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR)
	}

	if isThirdAccount && !IsThirdAccountType(req.ExtFrom) {
		log.Warnf("bind third account but with not support third-account-type: [%s]", req.ExtFrom)
		return errorcode.EC_ACCOUNT_BIND_THIRD_INVALID_ACCOUNT_TYPE, "invalid account type"
	}

	if !isThirdAccount && !IsSelfAccountType(req.ExtFrom) {
		log.Warnf("bind self account but with not support self-account-type : [%s]", req.ExtFrom)
		return errorcode.EC_ACCOUNT_BIND_SELF_INVALID_ACCOUNT_TYPE, "invalid account type"
	}

	var userid string
	var bindStatus int8
	if accountInfo != nil {
		userid = accountInfo.Userid
		bindStatus = accountInfo.BindStatus
	}

	log.Debugf("AccountBind for uid: %s, extFrom: %s, extId: %s", uid, req.ExtFrom, req.ExtId)

	//已绑定其他账号
	if bindStatus == AI_BIND_STATUS || (userid != "" && userid != uid) {
		log.Warnf("uid: %s bind failed for extFrom: %s, extId: %s, msg: already binded by other account", uid, req.ExtFrom, req.ExtId)
		return errorcode.EC_ACCOUNT_BIND_BINDED_BY_OTHER, "already binded yet"
	}

	//未绑定过,开始绑定到uid上
	if userid == "" || bindStatus == 0 {
		aichanger := NewAccountInfoChanger(req.ExtId, req.ExtFrom)
		aichanger.SetAccount(req.ExtId)
		aichanger.SetAccountType(req.ExtFrom)
		aichanger.SetUserid(uid)
		aichanger.SetActive(AI_ACTIVE_ALLOW_LOGIN)
		aichanger.SetNickName(req.NickName)

		if isThirdAccount {
			aichanger.SetThirdAccount(AI_THIRD_ACCOUNT)
		} else {
			aichanger.SetThirdAccount(AI_SELF_ACCOUNT)
		}
		aichanger.SetBindStatus(AI_BIND_STATUS)
		aichanger.SetInsertTime(time.Now().Format("2006-01-02 15:04:05"))
		if err := aichanger.Save(); err != nil {
			log.Errorf("SetAllAccountInfo error when bind, msg: %s", err.Error())
			return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR)
		}
		p.RecordUserAccountBind(uid, req.ExtFrom, req.ExtId)
	}
	return errorcode.EC_GL_SUCCESS, errorcode.Msg(errorcode.EC_GL_SUCCESS)
}

/*
	账号解绑
*/
func (p *AccountBindService) AccountUnbind(uid, extFrom, extId string) (uint, string) {
	log.Debugf("AccountUnbind for uid: %s, extFrom: %s, extId: %s", uid, extFrom, extId)
	accountInfo, err := GetAccountService().GetAllAccountInfo(extFrom, extId)
	if err != nil {
		log.Errorf("get accountinfo for extFrom: %s, extId: %s error: %s", extFrom, extId, err.Error())
		return 0, err.Error()
	}

	if !IsValidAccountType(extFrom) {
		return errorcode.EC_GL_INVALID_ACCOUNT_TYPE, errorcode.Msg(errorcode.EC_GL_INVALID_ACCOUNT_TYPE)
	}

	if accountInfo == nil { //账号不存在
		return errorcode.EC_ACCOUNT_UNBIND_NOT_EXISTS, "account is NOT exists"
	}

	if accountInfo.BindStatus == AI_UNBIND_STATUS { //账号已经解绑
		return errorcode.EC_ACCOUNT_UNBIND_NOT_EXISTS, "account is NOT exists"
	}

	if accountInfo.ThirdAccount == AI_SELF_ACCOUNT { //自有账号禁止解绑
		return errorcode.EC_ACCOUNT_UNBIND_FORBIDDEN_SELF, "forbidden unbind self account"
	}

	if accounts, err := p.GetBindedAccountForUserid(uid); err == nil {
		if len(accounts) <= 1 { //唯一账号禁止解绑
			return errorcode.EC_ACCOUNT_UNBIND_FORBIDDEN_UNIQ, "unbind failed for unique account"
		} else { //解绑操作
			conn := models.GetPassportModel().Get()
			defer conn.Close()
			conn.Send("del", GetAccountService().GetAccountKey(extFrom, extId))
			conn.Send("hdel", p.GetUserAccountBindKey(uid), extFrom)
			conn.Flush()
			if _, err := conn.Receive(); err == nil {
				conn.Receive()
				item := &xfile.PassportInfoItem{}
				item.TableName = xfile.PI_ACCOUNT
				item.Op = xfile.OP_DELETE
				deldata := make(map[string]interface{})
				deldata["account"] = extId
				deldata["account_type"] = extFrom
				item.Data = deldata
				xfile.GetPassportInfoMgr().SavePassportInfo(item)
			} else {
				log.Warnf("AccountUnbind error for uid: %s, msg: %s", uid, err.Error())
				return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR)
			}
			return errorcode.EC_GL_SUCCESS, errorcode.Msg(errorcode.EC_GL_SUCCESS)
		}
	} else {
		log.Warnf("GetBindedAccountForUserid error for uid: %s, msg: %s", uid, err.Error())
	}
	return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR)
}
